import java.text.Collator;
import java.util.Observable;
import java.util.Observer;

import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

/**
 * @author johan
 */
public class FontBook extends ApplicationWindow implements Observer {
    private Font currentFont;
    private FontItem currentFontItem;
    private FontNamePatternFilter searchFilter;
    private Text previewText;
    private ListViewer fontList;
    private FontContentProvider fontContentProvider;
    private Text searchBox;
    private Scale fontScaler;
    private Label currentFontInfo;
    private ListViewer fontListViewer;
    private static Display display;


    /**
     * @param parentShell
     */
    public FontBook() {
        super(new Shell());

        display = Display.getCurrent();
        searchFilter = new FontNamePatternFilter();
    }

    protected void setupDragAndDrop(Control target) {
        FontDropListener dropListener = new FontDropListener();

        // Order of observer adding is important: this.update must be called 
        // before getElements of the ContentProvider is called
        dropListener.addObserver(this);
        dropListener.addObserver(fontContentProvider);
        
        DropTarget dt = new DropTarget(target, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT);
        
        dt.setTransfer(new Transfer[] { FileTransfer.getInstance() });
        dt.addDropListener(dropListener);
    }

    /**
     * 
     *
     */
    public void run() {
        setBlockOnOpen(true);
        open();

        display.dispose();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
     */
    @Override
    protected void configureShell(Shell sh) {
        super.configureShell(sh);

        sh.setText("Font Book");
        sh.setSize(640, 480);
    }

    protected ListViewer createFontListViewer(Composite parent) {
        fontListViewer = new ListViewer(
            parent, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
        
        fontContentProvider = new FontContentProvider();

        fontListViewer.setUseHashlookup(true);
        fontListViewer.setSorter(new ViewerSorter(Collator.getInstance()));
        fontListViewer.setContentProvider(fontContentProvider);
        fontListViewer.setLabelProvider(new FontLabelProvider());
        fontListViewer.addFilter(searchFilter);
        fontListViewer.setInput("");
        
        List list = fontListViewer.getList();

        list.setLayoutData(new GridData(GridData.FILL_BOTH));
        list.select(0);
        list.pack();

        return fontListViewer;
    }

    protected Text createPreviewBox(Composite parent) {
        Text textBox = new Text(
            parent, 
            SWT.CENTER | SWT.BORDER | SWT.MULTI | SWT.WRAP);
        GridData layout = new GridData(
            GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);

        textBox.setLayoutData(layout);
        textBox.pack();

        return textBox;
    }

    protected Text createSearchBox(Composite parent) {
        Text searchBox = new Text(parent, SWT.BORDER);

        GridData searchBoxLayoutData = new GridData(GridData.GRAB_HORIZONTAL
                | GridData.FILL_HORIZONTAL);

        searchBox.setLayoutData(searchBoxLayoutData);
        searchBox.pack();

        return searchBox;
    }

    protected Scale createSizeScaler(Composite parent) {
        Scale sizeScale = new Scale(parent, SWT.VERTICAL);

        sizeScale.setLayoutData(new GridData(GridData.FILL_VERTICAL));
        sizeScale.setMinimum(8);
        sizeScale.setMaximum(72);
        sizeScale.setIncrement(2);
        sizeScale.setSelection(24);
        sizeScale.pack();

        return sizeScale;
    }

    @Override
    protected Control createContents(Composite comp) {
        Composite parent = new Composite(comp, SWT.NONE);
        GridLayout grid = new GridLayout();
        grid.numColumns = 3;
        parent.setLayout(grid);

        // Add search box
        searchBox = createSearchBox(parent);

        // Add font info box
        currentFontInfo = new Label(parent, SWT.CENTER);
        currentFontInfo
                .setLayoutData(new GridData(GridData.FILL_HORIZONTAL
                        | GridData.HORIZONTAL_ALIGN_END
                        | GridData.GRAB_HORIZONTAL));
        
        //Button b = new Button(lbl, SWT.NONE);
        //b.setText("Install");

        // Break to new row
        new Label(parent, SWT.NONE);

        // Create fonts tree
        fontList = createFontListViewer(parent);

        // Text box
        previewText = createPreviewBox(parent);

        previewText.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
                + "abcdefghijklmnopqrstuvwxyz\n" + "0123456789");

        // Create font size slider
        fontScaler = createSizeScaler(parent);

        // Add search listener
        searchBox.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                searchFilter.setPattern(((Text) e.getSource()).getText());
                fontList.refresh(false);
            }
        });

        // Add font-changing listener
        fontList.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent e) {
                StructuredSelection selection = (StructuredSelection) e.getSelection();
                FontItem fontItem = (FontItem) selection.getFirstElement();
                

                if (fontItem != null) {
                    currentFontItem = fontItem;

                    updateFont();
                }
            }
        });

        // Add size change listener
        fontScaler.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event e) {
                updateFont();
            }
        });

        // Select and use first font in list
        StructuredSelection selection = (StructuredSelection) fontList.getSelection();
        FontItem selectedFontItem = (FontItem) selection.getFirstElement();
        
        if (selectedFontItem != null) {
            currentFontItem = selectedFontItem;
        }

        updateFont();
        
        setupDragAndDrop(getShell());

        parent.update();
        parent.pack();

        return parent;
    }

    protected void updateFont() {
        if (currentFontItem == null) {
            return;
        }
        
        FontData fontData = new FontData(
            currentFontItem.getFontData().getName(), 
            fontScaler.getSelection(), 
            currentFontItem.getFontData().getStyle());
        
        if (fontData.equals(currentFontItem.getFontData())) {
            return;
        }
        
        Font newFont = new Font(display, fontData);
        
        // Update tree selection
        fontList.refresh();
        
        for (int i = 0; i < fontList.getList().getItemCount(); ++i) {
            FontItem fontItem = (FontItem) fontList.getElementAt(i);
            
            if (currentFontItem.equals(fontItem)) {
                fontList.getList().setSelection(i);
                break;
            }
        }

        // Update preview area
        previewText.setFont(newFont);
        previewText.redraw();

        // Update font info label
        currentFontInfo.setText(
            String.format(
                "%s (%s), %d pt", 
                currentFontItem.getFontData().getName(), 
                FontUtils.getFontStyles(currentFontItem.getFontData()), 
                fontScaler.getSelection()));
        currentFontInfo.redraw();
        
        

        // Dispose old font
        if (currentFont != null) {
            currentFont.dispose();
        }

        currentFont = newFont;
    }

    public static void main(String... args) {
        new FontBook().run();
    }

    public void update(Observable o, Object arg) {
        currentFontItem = (FontItem) arg;
        
        updateFont();
    }
}